//**********************************************************************
//	File:			SGD_HandleManager.hpp
//	Author:			Doug Monroe
//	Last Modified:	12/5/12
//	
//	Purpose:		To store data with unique identifiers
//
//**********************************************************************

// This .hpp can ONLY be included from SGD_HandleManager.h
#ifndef INC_SGD_HANDLE_MANAGER_HPP
#error	FILE "SGD_HandleManager.hpp" CANNOT BE INCLUDED EXPLICITLY
#else

#include <cassert>

//namespace SGD
//{
	
	//******************************************************************
	// CHandle Components
	//	- union interprets between handle and components
	//	- bitfield enforces how much data the component can store
	//	  rrrrrrrr iiiiiiii iiiiiiii iiiiiiii
	class CHandleDecoder
	{
		#define BITS_REUSE	(8)
		#define BITS_INDEX	(32 - BITS_REUSE)

		#define MAX_REUSES	(1 << BITS_REUSE)
		#define MAX_INDICES	(1 << BITS_INDEX)

		union UHandle
		{
			unsigned long		handle;									// CHandle data
	
			struct
			{
				unsigned long	index	: BITS_INDEX;					// index into the vector
				unsigned long	reuse	: BITS_REUSE;					// reuse number
			};
		};

	public:
		// CHandle Conversion
		//	- inline function attempts to optimize for speed
		static inline unsigned long HandleToReuse( CHandle handle )
		{
			UHandle convert = { handle.m_ulHandle };
			return convert.reuse;
		}
	
		static inline unsigned long HandleToIndex( CHandle handle )
		{
			UHandle convert = { handle.m_ulHandle };
			return convert.index;
		}
	
		static inline unsigned long CreateHandle( unsigned long reuse, unsigned long index )
		{
			assert( index <= MAX_INDICES && "CHandleDecoder::CreateHandle - index exceeds maximum value!" != 0 );
			UHandle convert = { };
			convert.reuse = reuse;
			convert.index = index;
			return convert.handle;
		}
	
		static inline unsigned long ReuseHandle( CHandle handle )
		{
			UHandle convert = { handle.m_ulHandle };
	
			if( convert.reuse == MAX_REUSES - 1 )
				convert.reuse = 1;
			else
				convert.reuse++;

			return convert.handle;
		}

		#undef	BITS_REUSE
		#undef	BITS_INDEX

		#undef	MAX_REUSES
		#undef	MAX_INDICES
	};
	//******************************************************************
	


	//******************************************************************
	// CONSTRUCTOR
	template< typename DataType >
	CHandleManager< DataType >::CHandleManager( void )
	{
		// Allocate some room in the vector
		m_vData.reserve( 4 );
	}
	//******************************************************************


	
	//******************************************************************
	// DESTRUCTOR
	template< typename DataType >
	CHandleManager< DataType >::~CHandleManager( void )
	{
		// Explicitly empty the containers (does not deallocate stored data)
		m_vData.clear();
		m_lFreeIndices.clear();
	}
	//******************************************************************
	

	
	//******************************************************************
	// STORE HANDLE
	// - store data into owner's bucket (reusing an empty spot if available)
	// - return the unique handle to the data
	template< typename DataType >
	CHandle CHandleManager< DataType >::StoreData( DataType data )
	{		
		// Are there any free spots available?
		if( m_lFreeIndices.empty() == false )
		{
			// Reuse the last handle in the bucket
			CHandle handle = m_lFreeIndices.back();
			m_lFreeIndices.pop_back();
			handle = CHandleDecoder::ReuseHandle( handle );
			assert( handle != INVALID_HANDLE && "CHandleManager::StoreData - new handle is invalid ... sorry!" != 0 );

			// Store the new data
			unsigned int index = CHandleDecoder::HandleToIndex( handle );
			m_vData[ index ].first	= handle;
			m_vData[ index ].second	= data;

			// Return the handle
			return handle;
		}
		else
		{
			// Create a new handle into the bucket
			CHandle handle = CHandleDecoder::CreateHandle( 1, m_vData.size() );
			assert( handle != INVALID_HANDLE && "CHandleManager::StoreData - new handle is invalid ... sorry!" != 0 );
		
			// Store the new data
			DataPair info;
			info.first		= handle;
			info.second		= data;
			m_vData.push_back( info );
		
			// Return the handle
			return handle;
		}
	}
	//******************************************************************
	

	
	//******************************************************************
	// GET HANDLE
	//	- return the data stored at the location indicated by the handle
	template< typename DataType >
	DataType& CHandleManager< DataType >::GetData( CHandle handle ) const
	{
		// Verify the parameter
		assert( handle != INVALID_HANDLE && "CHandleManager::GetData - handle is invalid" != 0 );

		// Access the data indicated by the handle
		unsigned int index = CHandleDecoder::HandleToIndex( handle );
		assert( index < m_vData.size() && "CHandleManager::GetData - handle has invalid index" != 0 );
	
		// Verify the handle still corresponds to the stored data
		const DataPair& info = m_vData[ index ];
		assert( info.first != INVALID_HANDLE && "CHandleManager::GetData - handle has been removed" != 0 );
		assert( handle == info.first && "CHandleManager::GetData - handle has expired (another asset is using the location)" != 0 );

		// Return the stored data
		return *(const_cast<DataType*>(&info.second));
	}
	//******************************************************************
	

	
	//******************************************************************
	// REMOVE HANDLE
	//	- remove the data stored at the location indicated by the handle
	//	- return the data
	template< typename DataType >
	DataType CHandleManager< DataType >::RemoveData( CHandle handle )
	{
		// Verify the parameter
		assert( handle != INVALID_HANDLE && "CHandleManager::GetData - handle is invalid" != 0 );

		// Access the data indicated by the handle
		unsigned int index = CHandleDecoder::HandleToIndex( handle );
		assert( index < m_vData.size() && "CHandleManager::RemoveData - handle has invalid index" != 0 );
	
		// Verify the handle still corresponds to the stored data
		DataPair info = m_vData[ index ];
		assert( info.first != INVALID_HANDLE && "CHandleManager::RemoveData - handle has already been removed" != 0 );
		assert( handle == info.first && "CHandleManager::RemoveData - handle has expired (another asset is using the location)" != 0 );


		// Erase data from the bucket
		m_vData[ index ].first	= INVALID_HANDLE;
		//memset( &m_vData[ index ].second, 0, sizeof( DataType ) );
		m_vData[ index ].second	= DataType();

		// Add the handle into the free list
		m_lFreeIndices.push_back( handle );

		// Return the data (in case they really want it)
		return info.second;
	}
	//******************************************************************



	//******************************************************************
	// FOR EACH
	//	- iterate through the data, calling the provided function for each
	template< typename DataType >
	template< typename TExtraInfo >
	void CHandleManager< DataType >::ForEach( bool (*pFunction)( CHandle handle, DataType& data, TExtraInfo* extra ), TExtraInfo* extra ) // const
	{
		// Verify the callback function
		assert( pFunction != NULL && "CHandleManager::ForEach - invalid function pointer" != 0 );

		//// Iterate through all the (valid)stored data
		//for( int i = 0, size = m_vData.size(); i < size; i++ )
		//{
		//	if( m_vData[i].first != INVALID_HANDLE )
		//		// Stop looping if the callback function returns false
		//		if( pFunction( m_vData[i].first, *const_cast< DataType* >(&m_vData[i].second), extra ) == false )
		//			break;
		//}
	}
	//******************************************************************
	


//}	// namespace SGD


#endif //INC_SGD_HANDLE_MANAGER_HPP
